﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace IOPerformanceVerifier
{
    public class PerformanceLogger
    {
        private Logger _logger;
        private FileStream _fileStream;
        private string _fileName;
        private int _bufferSize;
        private List<string> _instances;
        private List<PerformanceCounter> _performanceCounters;
        private Dictionary<string, CounterSample> _lastSampleList;
        private Dictionary<string, ThreadConfigutation> _threadConfigurationDictionary;

        public PerformanceLogger(Dictionary<string, ThreadConfigutation> threadConfigurationDictionary)
        {
            _logger = Logger.Instance();
            _instances = new List<string>();
            _threadConfigurationDictionary = threadConfigurationDictionary;
            foreach (ThreadConfigutation threadConfiguration in threadConfigurationDictionary.Values)
            {
                if (!_instances.Contains(threadConfiguration.LogicalDriveLetter))
                {
                    _instances.Add(threadConfiguration.LogicalDriveLetter);
                }
            }
        }

        public void initialize()
        {
            _logger.log(LogLevel.Debug, "Performance Logger initialization started");

            // TODO get data from configuration
            _bufferSize = 64 * 1024;
            // TODO create better name
            _fileName = "IOVerifierLog" + DateTime.Now.ToString("yyyyMMdd_HHmmssFFF") + ".iovl";

		    PerformanceCounterCategory performanceCounterCategory =
                new PerformanceCounterCategory("LogicalDisk");

            _performanceCounters = new List<PerformanceCounter>();
            if (performanceCounterCategory != null)
            {
                _logger.log(LogLevel.Debug, "PerformanceCounterCategory: " + performanceCounterCategory.CategoryName);
                // Retrieve the counters.
                PerformanceCounter[] performanceCounters;
                foreach (string instance in _instances)
                {
                    performanceCounters = performanceCounterCategory.GetCounters("E:");
                    foreach (PerformanceCounter performanceCounter in performanceCounters)
                    {
                        if (acceptedCounter(performanceCounter.CounterName))
                        {
                            _performanceCounters.Add(performanceCounter);
                        }
                    }
                }
            }
            else
            {
                _logger.log(LogLevel.Error, "No such Performance Counter Category");
            }
            _lastSampleList = new Dictionary<string, CounterSample>();

            _fileStream = new FileStream(_fileName, FileMode.Create, FileAccess.Write, FileShare.None, _bufferSize);
            _logger.log(LogLevel.Debug, "PerformanceLogger initialized");
        }

        private bool acceptedCounter(string performanceCounterName)
        {
            switch (performanceCounterName)
            {
                case "Avg. Disk Queue Length":
                case "Avg. Disk Read Queue Length":
                case "Avg. Disk Write Queue Length":
                case "Avg. Disk sec/Read":
                case "Avg. Disk sec/Write":
                case "Disk Reads/sec":
                case "Disk Writes/sec":
                case "Disk Read Bytes/sec":
                case "Disk Write Bytes/sec":
                case "Avg. Disk Bytes/Read":
                case "Avg. Disk Bytes/Write":
                case "Split IO/Sec": return true;
                default: return false;
            }
        }

        public bool startLogging()
        {
            // TODO make work
            // TODO Write columns
            _logger.log(LogLevel.Debug, "Performance Logging started.");
            return true;
        }

        public bool stopLogging()
        { 
            // TODO make work
            _logger.log(LogLevel.Debug, "Performance Logging stopped.");
            // Flush and dispose file stream
            if (_fileStream != null)
            {
                _fileStream.Flush();
                _fileStream.Dispose();
            }
            return true;
        }

        public void logPerformanceCounters()
        {
            // TODO make work
            System.Text.ASCIIEncoding encoding = new ASCIIEncoding();
            StringBuilder stringBuilder = new StringBuilder();
            if (_lastSampleList.Count > 0)
            {
                stringBuilder.Append(DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.FFF"));
            }
            else
            {
                stringBuilder.Append("[Date&Time]");
            }
            foreach (PerformanceCounter performanceCounter in _performanceCounters)
            {
                string performanceInstanceCounterName = performanceCounter.InstanceName + performanceCounter.CounterName;
                    CounterSample counterSample = performanceCounter.NextSample();

                    if (_lastSampleList.ContainsKey(performanceInstanceCounterName))
                    {
                        CounterSample lastCounterSample;
                        if (_lastSampleList.TryGetValue(performanceInstanceCounterName, out lastCounterSample))
                        {
                            stringBuilder.Append(";\"" + CounterSample.Calculate(lastCounterSample, counterSample) + "\"");
                            _lastSampleList[performanceInstanceCounterName] = counterSample;
                        }
                    }
                    else
                    {
                        stringBuilder.Append(";\"[" + performanceCounter.InstanceName + "\\" + performanceCounter.CounterName + "]\"");
                        _lastSampleList.Add(performanceInstanceCounterName, counterSample);
                    }
            }
            stringBuilder.AppendLine("");
            _fileStream.Write(encoding.GetBytes(stringBuilder.ToString()), 0, stringBuilder.Length);
            //_logger.log(LogLevel.Debug, "Performance Logging done.");
        }
    }
}
